// 1.将下面异步代码使用Promise的方式改进

// setTimeout(function (){
//     var a = 'hello'
//     setTimeout(function (){
//         var b = 'lagou'
//         setTimeout(function (){
//             var c = 'I Love U'
//             console.log(a + b + c)
//         },10)
//     },10)
// },10)

new Promise((resolve, reject) => {
    var a = 'hello'
    setTimeout(() => {
        resolve(a)
    },10)
}).then(a => {
    var b = 'lagou'
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(a + b)
        },10)
    })
}).then((data) => {
    var c = 'I Love U'
    setTimeout(() => {
        console.log(data + c)
    },10)
})

// 2.基于以下代码完成下面的四个练习
const fp = require('lodash/fp')
const cars = [
    {name:'Ferrart FF',horsepower:660,dollar_value:700000,in_stock:true},
    {name:'Spyker C12 Zagato',horsepower:650,dollar_value:648000,in_stock:false},
    {name:'Jagur XKR-S',horsepower:550,dollar_value:132000,in_stock:false},
    {name:'Audi R8',horsepower:525,dollar_value:114200,in_stock:false},
    {name:'Aston Martin One-77',horsepower:750,dollar_value:1850000,in_stock:true},
    {name:'Pagani Huayra',horsepower:700,dollar_value:1300000,in_stock:false},
]

//练习1：使用函数组合 fp.flowRight() 重新实现下面这个函数
let isLastInStock = function (cars){
    //获取最后一条数据
    let last_car = fp.last(cars)
    //获取最后一条数据的 in_stock 属性值
    return fp.prop('in_stock', last_car)
}

let isLastInStock_fp = fp.flowRight(fp.curry(fp.prop)('in_stock'), fp.last)

//练习2：使用fp.flowRight(),fp.prop()和fp.first()获取第一个car的name
let firstCarName_fp = fp.flowRight(fp.curry(fp.prop)('name'), fp.first)

//练习3： 使用帮助函数_average重构averageDollarValue,使用函数组合的方式实现
let _average = function (xs){
    return fp.reduce(fp.add, 0, xs)/xs.length
}

let averageDollarValue = function (cars){
    let dollar_values = fp.map(function (car){
        return car.dollar_value
    }, cars)
    return _average(dollar_values)
}
let averageDollarValue_fp = fp.flowRight(_average, fp.curry(fp.map)(fp.prop('dollar_value')))

//练习4：使用flowRight写一个sanitizeNames()函数，返回一个下划线连接的小写字符串，把数组中的name转换为这种形式，例如：
//sanitizeNames(["Hello World"]) => ["hello_world"]
let _underscore = fp.replace(/\w+/g, '_')
//console.log(_underscore(["Hello World"]))
let sanitizeNames = fp.flowRight(_underscore, fp.toLower, fp.curry(fp.map)(fp.prop('name')))
console.log(sanitizeNames(cars))


// 3.基于下面提供的代码，完成后续的四个练习
class Container {
    static of(value){
        return new Container(value)
    }

    constructor(value){
        this._value = value
    }

    map(fn){
        return Container.of(fn(this._value))
    }
}

class Maybe {
    static of(x){
        return new Maybe(x)
    }

    constructor(x){
        this._value = x
    }

    isNothing(){
        return this._value === null || this._value === undefined
    }

    map(fn){
        return this.isNothing() ? this : Maybe.of(fn(this._value))
    }
}
// module.exports = { Maybe, Container }

//练习1：使用fp.add(x, y)和fp.map(f, x)创建一个能让functor里的值增加的函数ex1
let maybe = Maybe.of([5, 6, 1])
let ex1 = () => {
    let curried_add = fp.curry(fp.add)(1)
    let curried_map = fp.curry(fp.map)(curried_add)
    maybe = maybe.map(curried_map)
}
// console.log(maybe)
// ex1()
// console.log(maybe)
// ex1()
// console.log(maybe)
// ex1()
// console.log(maybe)

//练习2：实现一个函数ex2，能够使用fp.first获取列表的第一个元素
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do'])
let ex2 = () => {
    return xs.map(fp.first)._value
}
//console.log(ex2())

//练习3：实现一个函数ex3，使用safeProp和fp.first找到user的名字的首字母
let safeProp = fp.curry(function (x, o){
    return Maybe.of(o[x])
})
let user = { id: 2, name: 'Albert' }
let ex3 = () => {
    return fp.first(safeProp('name',user)._value)
}
//console.log(ex3())

//练习4：使用Maybe重写ex4，不要有if语句
let ex4 = function (n){
    if(n){
        return parseInt(n)
    }
}

let maybe_ex4 = (n) => {
    return Maybe.of(n).map((n) => parseInt(n))._value
}
//console.log(maybe_ex4())


// 4.手写实现MyPromise源码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    constructor (executor){
        try{
            executor(this.resolve, this.reject)
        }catch (err){ //捕获执行器得错误
            this.reject(err)
        }
    }

    status = PENDING //记录状态( pending | fulfilled | rejected )
    value = undefined //保存resolve(data)执行时传的参数
    reason = undefined //保存reject(err)执行时传的错误(或者说错误原因)
    fulfilledCallback = [] //保存then(fulfilledCallback,rejectedCallback)执行时的fulfilledCallback函数
    rejectedCallback = [] //保存then(fulfilledCallback,rejectedCallback)执行时的rejectedCallback函数

    resolve = value => {
        if(this.status !== PENDING) return //如果状态不为pending，则拒绝修改状态
        this.status = FULFILLED //修改状态为成功
        this.value = value
        console.log(this.fulfilledCallback.length)
        //判断fulfilledCallback是否为空，不为空就依次执行回调函数
        while(this.fulfilledCallback.length) this.fulfilledCallback.shift()()
    }

    reject = reason => {
        if(this.status !== PENDING) return //如果状态不为pending，则拒绝修改状态
        this.status = REJECTED //修改状态为失败
        this.reason = reason
        //判断rejectedCallback是否为空，不为空就依次执行回调函数
        while(this.rejectedCallback.length) this.rejectedCallback.shift()()
    }

    then = (fulfilledCallback, rejectedCallback) => {
        fulfilledCallback = fulfilledCallback ? fulfilledCallback : value => value //设置一个默认得传值回调
        rejectedCallback = rejectedCallback ? rejectedCallback : reason => { throw resaon }
        let promise2 = new MyPromise((resolve, reject) => {
            if(this.status === FULFILLED){ //成功状态
                setTimeout(() => { //用定时器将代码变成异步代码，因为同步情况下promise2对象还未创建完成
                    try{
                        let x = fulfilledCallback(this.value)
                        resolvePromise(promise2, x, resolve, reject)//不使用定时器的话此时还没有promise2对象
                    }catch (err){
                        reject(err)
                    }
                },0)
            }else if(this.status === REJECTED){ //失败状态
                setTimeout(() => { //用定时器将代码变成异步代码，因为同步情况下promise2对象还未创建完成
                    try{
                        let x = rejectedCallback(this.reason)
                        resolvePromise(promise2, x, resolve, reject)//不使用定时器的话此时还没有promise2对象
                    }catch (err){
                        reject(err)
                    }
                },0)
            }else{ //pending状态，此时异步任务还未结束，先将回调函数缓存，等到状态确定时再调用
                this.fulfilledCallback.push(() => {
                    setTimeout(() => { //用定时器将代码变成异步代码，因为同步情况下promise2对象还未创建完成
                        try{
                            let x = fulfilledCallback(this.value)
                            resolvePromise(promise2, x, resolve, reject)//不使用定时器的话此时还没有promise2对象
                        }catch (err){
                            reject(err)
                        }
                    },0)
                })
                this.rejectedCallback.push(() => {
                    setTimeout(() => { //用定时器将代码变成异步代码，因为同步情况下promise2对象还未创建完成
                        try{
                            let x = rejectedCallback(this.reason)
                            resolvePromise(promise2, x, resolve, reject)//不使用定时器的话此时还没有promise2对象
                        }catch (err){
                            reject(err)
                        }
                    },0)
                })
            }
        })
        return promise2
    }

    finally (callback) {
        return this.then(value => {
            return MyPromise.resolve(callback()).then(() => value)
        }, reason => {
            return MyPromise.resolve(callback()).then(() => { throw reason })
        })
    }

    catch (failCallback){
        return this.then(undefined, failCallback)
    }

    static all(arr){ //MyPromise.all()方法需要传入一个数组,返回一个MyPromise对象
        let result = []
        let index = 0
        return new MyPromise((resolve, reject) => {
            function addData(key,value){
                result[key] = value
                index ++
                if(index === arr.length){
                    resolve(result)
                }
            }

            for (let i = 0; i < arr.length; i++){ //循环遍历传入的数组
                let current = arr[i]
                if(current instanceof MyPromise){ //判断是普通值还是promise对象
                    //promise对象
                    current.then(value => addData(i, value), reason => reject(reason))
                }else{
                    //普通值
                    addData(i,current)
                }
            }
        })
    }

    static resolve (value){
        if(value instanceof MyPromise) return value
        return new MyPromise(resolve => resolve(value))
    }
}

function resolvePromise (promise2, x, resolve, reject){
    if(promise2 === x){ // let p1 = promise.then((data) => {return p1}) 判断循环调用得情况
        return reject(new TypeError('Chainning cycle detected for promise'))
    }
    if (x instanceof MyPromise){ //promise对象
        x.then(resolve, reject)
    }else{
        resolve(x) //普通值
    }
}